package nila;
import java.util.*;

enum T_Sym {
    SYM, STRING, NUM, SEMI, PIPE, EOF
};


interface I_Parse {
    I_Expr next();
}

interface I_Expr {
    String v();
}

interface I_Sym {
    T_Sym t();
    String v();
}

class NSymbol implements I_Sym {
    int _sym;
    T_Sym _t;
    public NSymbol(int s) {
        _sym = s;
        _t = T_Sym.SYM;
    }
    public NSymbol(T_Sym t) {
        _t = t;
    }

    public T_Sym t() {
        return _t;
    }
    public String v() {
        if (_t == T_Sym.SYM)
            return Sym.v(_sym);
        return _t.toString();
    }
}

class NNumber implements I_Sym {
    Number _num;
    public NNumber(Number i) {
        _num = i;
    }
    public T_Sym t() {
        return T_Sym.NUM;
    }
    public String v() {
        return _num.toString();
    }
}

class NString implements I_Sym {
    String _str;
    public NString(String s) {
        _str = s;
    }
    public T_Sym t() {
        return T_Sym.STRING;
    }
    public String v() {
        return _str;
    }
}

public class Parse implements I_Parse {
    List<I_Sym> _uneval = null;
    I_Reader _reader = null;
    I_Lex _lex = null;
    ASTContext _context = null;
    public Parse(I_Reader r) {
        // Establish the initial context.
        _reader = r;
        _lex = new Lex(r);
        _uneval = new LinkedList<I_Sym>();
        _context = new ASTContext();
    }
    
    public static boolean debug = false;
    void d(String v) {
        if (debug) I.d("parse", v);
    }

    List<I_Sym> tape() {
        return _uneval;
    }
    boolean parse() {
        _reader.push_prompt("| ");
        T_Lex l = _lex.next();
        while (l != T_Lex.EOF) {
            switch (l) {
                case SYM: {
                              int i = _lex.read_sym();
                              d("Sym:" + i + ":"  + Sym.v(i));
                              _uneval.add(new NSymbol(i));
                          } break;
                          //case STRING: I.out("String"); break;
                case NUM: {
                              Number i = _lex.read_num();
                              d("Num" + i.toString());
                              _uneval.add(new NNumber(i));
                          } break;
                case NL: {
                             d("Newline");
                             return true;
                }
                case SEMI: {
                               d("Semi");
                               _uneval.add(new NSymbol(T_Sym.SEMI));
                           } break;
                case SHARP: I.out("Sharp"); break;
                case RBRACE: I.out("Rbrace"); break;
                case LBRACE: I.out("Lbrace"); break;
                case RSQUARE: I.out("Rsq"); break;
                case LSQUARE: I.out("Lsq"); break;
                case RPAREN: I.out("Rparen"); break;
                case LPAREN: I.out("Lparen"); break;
                case RSLASH: I.out("Rslash"); break;
                case LSLASH: I.out("Lslash"); break;
                case QUOTE: {
                                String s = _lex.read_str("'");
                                d("String:" + s);
                                _uneval.add(new NString(s));
                            } break;
                case DQUOTE: {
                                 String s = _lex.read_str("\"");
                                 d("String:" + s);
                                 _uneval.add(new NString(s));
                             } break;
                case TICK: I.out("Tick"); break;
                case EOF: I.out("eof"); break;
                case PIPE: {
                               d("Pipe");
                               _uneval.add(new NSymbol(T_Sym.PIPE));
                           } break;
                default: I.out("Got something.");
            }
            d("before next");
            l = _lex.next();
            d("after next" );
        }
        return false;
    }
/*
                c1
                |a_stream
                |
                V[c2.in]
                c2
                |out
                |
                V[$var1]
                |
                V[c3.in]
                c3
                |out
                |
                V[$var3]
                c4[ var3 > 5]
*/
/*
    :def v1> v2> v3> <v4 <v5 var1 var2 var3
        c1:cmd1/spec1/spec2:arg | c2:cmd2 c1.a_stream>c2.in c2.out>var1;
        c3:cmd3 $var1>c3.in c3.out>var3;
        c4:cmd4/while:{var3 > 5}/do:{var3 - 5}
*/
/*
    Net           : NodeList (<;> <NodeList>)*
    NodeList      : Node (<|> Node)*
    Node          : Sym ArgList
    ArgList       : (Sym | String | Num)*
*/
    void do_semi() {
        I_Sym s = _uneval.remove(0);
        switch (s.t()) {
            case SEMI:
                break;
            default :
                throw new Xn("Expect Semi " + s.v());
        }
    }
    void do_pipe() {
        I_Sym s = _uneval.remove(0);
        switch (s.t()) {
            case PIPE:
                break;
            default :
                throw new Xn("Expect Pipe " + s.v());
        }
    }

    void do_net() {
        _context.start_net();
        while(true) {
            do_nodelist();
            if (_uneval.size() == 0) break;
            do_semi();
        }
        _context.stop_net();
    }

    void do_nodelist() {
        _context.start_nodelist();
        boolean b = false;
        while(!b) {
            do_node();
            if (_uneval.size() == 0) b=true;
            else {
                I_Sym s = _uneval.get(0);
                switch (s.t()) {
                    case SEMI:
                        b=true;
                        break;
                    case PIPE:
                        do_pipe();
                        break;
                    default :
                        throw new Xn("Expect Semi " + s.v());
                }
            }
        }
        _context.stop_nodelist();
    }

    void do_sym() {
        I_Sym s = _uneval.remove(0);
        switch (s.t()) {
            case SYM:
                break;
            default :
                throw new Xn("Expect Sym " + s.v());
        }
        _context.add_sym(s);
    }

    void do_node() {
        _context.start_node();
        do_sym();
        do_arglist();
        _context.stop_node();
    }

    void do_arglist() {
        _context.start_arglist();
        boolean b = false;
        while(!b) {
            if (_uneval.size() == 0) b=true;
            else {
                I_Sym s = _uneval.get(0);
                switch (s.t()) {
                    case SYM:
                    case STRING:
                    case NUM:
                        _uneval.remove(0);
                        _context.add_arg(s);
                        break;
                    case PIPE:
                    case SEMI:
                        b=true;
                        break;
                    default :
                        throw new Xn("Expect Sym|String|Num " + s.v());
                }
            }
        }
        _context.stop_arglist();
    }

    public I_Expr next() {
        if (!parse()) return null;
        if (_uneval.size() == 0) return  new I_Expr() {public String v(){return "..";}};
        do_net();
        return new I_Expr() {public String v(){return "..";}};
    }
}

